﻿using Core.DataAccess.Model.Project.Logs;
using Core.DataAccess.Model.ProjectLogs;
using Core.Framework.EntityExtend;
using Core.Framework.Model.Common;
using Core.Framework.Util;
using Core.Framework.Util.Common;
using Core.IBusiness.ILoggerModule;
using Quartz;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;

namespace Core.Business.LoggerModule
{
    public class Loggers : ILog
    {

        /// <summary>
        /// 写入日志
        /// </summary>
        class ExecInsertLogger : ITimerJob
        {
            public void Execute(IJobExecutionContext jobContext)
            {
                if (loggers.Count() > 0)
                {
                    using (var context = new ProjectLogsContext())
                    {
                        lock (oLogger)
                        {
                            context.Logger.BulkInsert(loggers);
                            loggers = new List<Logger>();
                        }

                        var row = context.BeginSaveChanges(out string msg);

                        if (row == 0 && !string.IsNullOrEmpty(msg))
                        {
                            Logger entity = new Logger();
                            entity.ProjectToken = CoreStartupHelper.GetConfigValue("Service:Title");
                            entity.AddTime = DateTime.Now;
                            entity.Content = new
                            {
                                type = $"Core.Business.LoggerModule.Loggers",
                                value = msg
                            }.TryToJson();
                            loggers.Add(entity);
                        }
                    }
                }
            }
        }

        static Loggers()
        {
            var job = new TimerJob();

            job.RegisterDisallowConcurrent<ExecInsertLogger>(a => 
                a.WithIntervalInSeconds(10)
                    .RepeatForever()
                );
            
        }

        static object oLogger = new object();

        static List<Logger> loggers = new List<Logger>();

        /// <summary>
        /// 添加日志到缓存
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="type"></param>
        private void AddLogger(Logger entity, Type type)
        {
            entity.Content = new
            {
                Tag = entity.Tag,
                Template = entity.Template,
                type = type == null ? "" : $"{type.Namespace}.{type.Name}",
                value = entity.Content
            }.TryToJson();

            lock (oLogger)
            {
                loggers.Add(entity);
            }
        }

        private IEnumerable<Logger> GetEntitys(
            Expression<Func<Logger, bool>> lamdba, 
            Pagination pagination,
            bool isGroup = false,
            string nottag = "",
            string template = "")
        {

            using (var context = new ProjectLogsContext())
            {

                pagination.records
                    = context.Logger.Where(lamdba).Count();

                var data = context
                    .Logger
                    .Where(lamdba)
                    .OrderByDescending(a => a.Id)
                    .Skip(pagination.rows * (pagination.page > 0 ? pagination.page - 1 : 0))
                    .Take(pagination.rows)
                    .ToList();

                if (isGroup)
                {
                    foreach (var item in data)
                    {
                        item.ChildLoggers = context
                            .Logger
                            .Where(a => a.Tag != nottag && a.Template == template && a.LogFlag == item.LogFlag)
                            .Select(a => new
                            {
                                a.Template,
                                a.LogFlag,
                                a.Content,
                                a.Tag,
                                a.Type,
                                a.AddTime
                            })
                            .OrderBy(a => a.AddTime)
                            .ToList();

                        item.ChildCount = item.ChildLoggers.Count();
                    }
                }

                return data;

            }
        }



        public void Info<T>(string value, string projectToken)
        {
            this.AddLogger(new Logger
            {
                Tag = "info",
                Template = "common",
                Content = value,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }

        public void DeBug<T>(string value, string projectToken)
        {
            this.AddLogger(new Logger
            {
                Tag = "debug",
                Template = "common",
                Content = value,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }

        public void Warning<T>(string value, string projectToken)
        {
            this.AddLogger(new Logger
            {
                Tag = "warning",
                Template = "common",
                Content = value,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }

        public void Error<T>(string value, string projectToken)
        {
            this.AddLogger(new Logger
            {
                Tag = "error",
                Template = "common",
                Content = value,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }



        public void Queue<T>(string value, string tag, string projectToken, string logFlag = "")
        {
            this.AddLogger(new Logger
            {
                Template = "queue",
                Tag = tag,
                Content = value,
                LogFlag = logFlag,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }

        public void Request<T>(string value, string tag, string projectToken, string logFlag = "")
        {
            this.AddLogger(new Logger
            {
                Template = "request",
                Tag = tag,
                Content = value,
                LogFlag = logFlag,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }

        public void SqlServer<T>(string value, string tag, string projectToken, string logFlag = "")
        {
            this.AddLogger(new Logger
            {
                Template = "sqlserver",
                Tag = tag,
                Content = value,
                LogFlag = logFlag,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }

        public void Customize<T>(string tag, string template, string value, string projectToken, string logFlag = "")
        {
            this.AddLogger(new Logger
            {
                Template = template,
                Tag = tag,
                Content = value,
                LogFlag = logFlag,
                ProjectToken = projectToken,
                AddTime = DateTime.Now
            }, typeof(T));
        }





        public IEnumerable<Logger> GetLoggers(Pagination pagination, DateTime starTime, DateTime endTime, string projectToken, string template = "", string tag = "", string tagFlag = "", string search = "")
        {

            Expression<Func<Logger, bool>> lamdba = (a) => 
                a.ProjectToken == projectToken;


            lamdba = lamdba.And(a => a.AddTime >= starTime && a.AddTime <= endTime);

            if (!string.IsNullOrEmpty(tag))
                lamdba = lamdba.And(a => a.Tag == tag);

            if (!string.IsNullOrEmpty(search))
                lamdba = lamdba.And(a => a.LogFlag == search || a.Content.Contains(search));


            if (!string.IsNullOrEmpty(template))
                lamdba = lamdba.And(a => a.Template == template);
            else
                lamdba = lamdba.And(a => a.Template != "queue");

            return GetEntitys(lamdba, pagination);
        }

        public IEnumerable<Logger> GetRequestLoggers(Pagination pagination, string projectToken, string search = "")
        {

            Expression<Func<Logger, bool>> lamdba = (a) => a.ProjectToken == projectToken && a.Template == "request";

            if (!string.IsNullOrEmpty(search))
                lamdba = lamdba.And(a => a.LogFlag == search || a.Content.Contains(search));

            return GetEntitys(lamdba, pagination);
        }

        /// <summary>
        /// 按组查询
        /// </summary>
        /// <param name="pagination"></param>
        /// <param name="search"></param>
        /// <returns></returns>
        public IEnumerable<Logger> GetQueueLoggers(Pagination pagination, DateTime starTime, DateTime endTime, string projectToken, string search = "")
        {

            Expression<Func<Logger, bool>> lamdba = (a) => a.ProjectToken == projectToken && a.Template == "queue" && a.Tag == "request";

            if (!string.IsNullOrEmpty(search))
                lamdba = lamdba.And(a => a.LogFlag == search || a.Content.Contains(search));

            lamdba = lamdba.And(a => a.AddTime >= starTime && a.AddTime <= endTime);

            return GetEntitys(lamdba, pagination, true, nottag:"request", template:"queue");
        }

        public IEnumerable<Logger> GetSqlServerLoggers(Pagination pagination, string projectToken, string search = "")
        {

            Expression<Func<Logger, bool>> lamdba = (a) => a.ProjectToken == projectToken && a.Template == "sqlserver";

            if (!string.IsNullOrEmpty(search))
                lamdba = lamdba.And(a => a.LogFlag == search || a.Content.Contains(search));

            return GetEntitys(lamdba, pagination);
        }

        public IEnumerable<string> GetTemplates(string template, string projectToken)
        {
            using (var context = new ProjectLogsContext())
            {
                return context
                    .Logger
                    .Where(a => a.ProjectToken == projectToken && a.Template == template)
                    .GroupBy(a => a.Template)
                    .Select(a => a.Key)
                    .ToList();
            }
        }

        public IEnumerable<string> GetTags(string template, string projectToken)
        {
            using (var context = new ProjectLogsContext())
            {
                return context
                    .Logger
                    .Where(a => a.ProjectToken == projectToken && !string.IsNullOrWhiteSpace(a.Tag) && a.Template == template)
                    .GroupBy(a => a.Tag)
                    .Select(a => a.Key)
                    .ToList();
            }
        }

    }
}
